package com.Cychat.gateway.filter;

import com.Cychat.commons.utils.JWTUtil;
import com.Cychat.commons.utils.TokenEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Date;

/**
 * 全局认证过滤器 - 处理HTTP请求
 * 功能：
 * 1. 拦截非白名单的HTTP请求
 * 2. 验证token并解析userId
 * 3. 将userId注入到请求头中
 */
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private final RedisTemplate<String, Object> redisTemplate;
    // 白名单路径
    @Value("#{'${auth.white.list}'.split(',')}")
    private List<String> whiteList;

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    // 构造函数注入RedisTemplate
    public AuthGlobalFilter(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        log.info("AuthGlobalFilter - 请求路径: {}", path);

        // WebSocket请求由WebSocketAuthFilter处理，这里直接放行
        if (isWebSocketRequest(request)) {
            log.info("WebSocket请求，跳过AuthGlobalFilter");
            return chain.filter(exchange);
        }

        // 白名单路径直接放行
        if (isWhiteListed(path)) {
            log.info("白名单路径，直接放行: {}", path);
            return chain.filter(exchange);
        }

        // 获取token
        Map<String, String> tokens = extractToken(request);
        String token = tokens.get("token");
        String retoken = tokens.get("retoken");

        if (token == null || token.isEmpty()) {
            log.warn("请求缺少token: {}", path);
            return unauthorizedResponse(exchange, "缺少认证token");
        }

        // 混合验证策略：先进行JWT签名验证（快速过滤无效token）
        TokenEnum tokenStatus = JWTUtil.verify(token);
        
        // 对于签名无效的token，直接拒绝，无需查Redis
        if (tokenStatus == TokenEnum.TOKEN_BAD) {
            log.warn("Token签名无效: {}", path);
            return unauthorizedResponse(exchange, "Token无效");
        }

        // 对于有效或过期的token，检查黑名单（只有通过签名验证的才查Redis）
        String tokenTid = JWTUtil.getJti(token);
        if (tokenTid != null && redisTemplate.hasKey("lastlist:jwt:" + tokenTid)) {
            log.warn("Token已被登出，拒绝访问: {}", path);
            return unauthorizedResponse(exchange, "Token已失效");
        }

        String newToken;
        Long userId = null;

        if (tokenStatus == TokenEnum.TOKEN_EXPIRE) {
            log.warn("Token已过期: {}", path);
            // 检查refresh token是否存在且未过期
            if (retoken != null && !retoken.isEmpty()) {
                // 验证refresh token
                TokenEnum retokenStatus = JWTUtil.verify(retoken);
                if (retokenStatus != TokenEnum.TOKEN_SUCCESS) {
                    log.warn("Refresh token无效或已过期");
                    return unauthorizedResponse(exchange, "Token已过期且无法刷新");
                }

                // 获取refresh token的jti
                String retokenTid = JWTUtil.getJti(retoken);
                // 检查redis中refresh token是否存在
                Object refreshTokenObj = redisTemplate.opsForValue().get("refresh_token_id:" + retokenTid);
                if (refreshTokenObj != null) {
                    log.info("refresh token有效，刷新token");
                    
                    // 从retoken中获取userId（更安全）
                    userId = JWTUtil.getuid(retoken);
                    if (userId == null || userId <= 0) {
                        log.warn("无法从refresh token中获取有效的userId");
                        return unauthorizedResponse(exchange, "Token刷新失败");
                    }

                    // 生成新的token
                    newToken = JWTUtil.generateToken(userId);

                    // 将新的token存储到redis中
                    String newTokenTid = JWTUtil.getJti(newToken);
                    redisTemplate.opsForValue().set("token_id:" + newTokenTid,
                            "validToken", Duration.ofMillis(JWTUtil.TOKEN_EXPIRE_TIME));

                    // 将旧token加入黑名单（如果还能解析）
                    if (tokenTid != null) {
                        Date oldTokenExp = JWTUtil.getExpiration(token);
                        if (oldTokenExp != null) {
                            long expireTime = oldTokenExp.getTime() - System.currentTimeMillis();
                            if (expireTime > 0) {
                                redisTemplate.opsForValue().set("lastlist:jwt:" + tokenTid,
                                        "refreshed", Duration.ofMillis(expireTime));
                            }
                        }
                    }

                    log.info("Token刷新成功，新token已生成，userId: {}", userId);
                } else {
                    newToken = null;
                    log.warn("Refresh token已过期或无效");
                    return unauthorizedResponse(exchange, "Token已过期且无法刷新");
                }
            } else {
                newToken = null;
                log.warn("缺少refresh token");
                return unauthorizedResponse(exchange, "Token已过期且无法刷新");
            }
        } else {
            // Token有效（TOKEN_SUCCESS），正常解析userId
            newToken = null;
            try {
                userId = JWTUtil.getuid(token);
                if (userId == null || userId <= 0) {
                    log.warn("Token中的userId无效: {}", userId);
                    return unauthorizedResponse(exchange, "无效的用户信息");
                }
            } catch (Exception e) {
                log.error("解析token时出错", e);
                return unauthorizedResponse(exchange, "Token解析失败");
            }
        }

        // 验证userId
        if (userId == null || userId <= 0) {
            log.warn("无法获取有效的userId");
            return unauthorizedResponse(exchange, "无效的用户信息");
        }

        log.info("Token验证成功，userId: {}", userId);

        // 将userId注入到请求头中
        ServerHttpRequest.Builder requestBuilder = request.mutate()
                .header("X-User-Id", String.valueOf(userId));

        // 如果token被刷新了，将新token添加到请求头（供后端服务使用）
        if (newToken != null) {
            requestBuilder.header("X-New-Token", newToken);
        }

        ServerHttpRequest modifiedRequest = requestBuilder.build();

        // 将userId注入到路径中
        String newPath = injectUserIdIntoPath(path, userId);
        modifiedRequest = modifiedRequest.mutate()
                .path(newPath)
                .build();

        log.info("修改后的请求路径: {}", modifiedRequest.getPath());
        ServerWebExchange modifiedExchange = exchange.mutate()
                .request(modifiedRequest)
                .build();

        // 如果token被刷新，需要在响应头中添加新token
        if (newToken != null) {
            return chain.filter(modifiedExchange).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                // 使用与登录时一致的响应头名称
                response.getHeaders().set("authorization", newToken);
                response.getHeaders().set("Access-Control-Expose-Headers", "authorization, retoken");
                log.info("新token已添加到响应头");
            }));
        }

        return chain.filter(modifiedExchange);
    }

    /**
     * 判断是否为白名单路径
     */
    private boolean isWhiteListed(String path) {
        return whiteList.stream().anyMatch(pattern -> pathMatcher.match(pattern, path));
    }

    /**
     * 判断是否为WebSocket请求
     */
    private boolean isWebSocketRequest(ServerHttpRequest request) {
        String upgrade = request.getHeaders().getFirst("Upgrade");
        return "websocket".equalsIgnoreCase(upgrade);
    }

    /**
     * 从请求中提取token
     * 支持两种方式：
     * 1. Authorization头: Bearer {token}
     * 2. 请求参数: token={token}
     */
    private Map<String, String> extractToken(ServerHttpRequest request) {
        Map<String, String> tokensMap;
        String token = null;
        String retoken = null;
        // 优先从请求头获取
        String tokenHeader = request.getHeaders().getFirst("authorization");
        if (tokenHeader != null && tokenHeader.startsWith("Bearer ")) {
            token = tokenHeader.substring(7);
        }
        String retokenHeader = request.getHeaders().getFirst("retoken");
        if (retokenHeader != null && !retokenHeader.startsWith("Bearer ")) {
            // 如果retoken不是Bearer格式，直接使用
            retoken = retokenHeader;
        } else if (retokenHeader != null && retokenHeader.startsWith("Bearer ")) {
            // 如果是Bearer格式，去掉前缀
            retoken = retokenHeader.substring(7);
        }
        // 其次从请求参数获取
        List<String> tokenParams1 = request.getQueryParams().get("token");
        if (tokenParams1 != null && !tokenParams1.isEmpty()) {
            token = tokenParams1.get(0);
        }
        List<String> tokenParams2 = request.getQueryParams().get("retoken");
        if (tokenParams2 != null && !tokenParams2.isEmpty()) {
            retoken = tokenParams2.get(0);
        }
        tokensMap = new HashMap<>();
        tokensMap.put("token", token);
        tokensMap.put("retoken", retoken);
        return tokensMap;
    }

    /**
     * 返回401未授权响应
     */
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");

        String body = String.format("{\"code\":401,\"msg\":\"%s\",\"data\":null}", message);
        return response.writeWith(Mono.just(response.bufferFactory().wrap(body.getBytes())));
    }

    /**
     * 设置过滤器优先级
     * 数值越小优先级越高
     * WebSocketAuthFilter 为 -1，这个为 0，确保WebSocket过滤器先执行
     */
    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 将userId注入到路径中
     * 例如: /api/chat/messages -> /api/chat/messages/{userId}
     */
    private String injectUserIdIntoPath(String originalPath, Long userId) {
        // 如果路径已经包含userId参数，则不重复添加
        if (originalPath.contains("/" + userId)) {
            return originalPath;
        }

        // 在路径末尾添加userId
        if (originalPath.endsWith("/")) {
            return originalPath + userId;
        } else {
            return originalPath + "/" + userId;
        }
    }
}
